/**
 * 
 */

var EMPTY_VALUES = {};
EMPTY_VALUES = Object.defineProperties(EMPTY_VALUES, {
        OBJECT: {
            get: function () {
                return {}
            }
        },
        ARRAY: {
            get: function () {
                return [];
            }
        },
        STRING: {
            get: function () {
                return '';
            }
        }
});


function p1(arr) {
    return arr.length == 1;
}

function isFunction(f) {
	return typeof f === 'function';
}

function isNumber(i) {
    return typeof i === 'number';
}

function fnExist(c) {
	return isFunction(c);
}
// 手写！！容易出错和漏掉，还是用函数吧
// undefined == null : true
// undefined === null : false
function oExist(o) {
	return o != null;
}

// not less than : >=
function pnl2(arr) {
    return arr.length >= 2;
}

function lt(a, b) {
    checkNumberType(a, b);
    return a < b;
}

function nlt(a, b) {
    return !lt(a, b);
}

// not less than
function pnl(arr, n) {
    return nlt(len(arr), n);// len(arr) >= n;
}

// check multiple number type is all number!
function checkNumberType(a) {
    if (p1(arguments)) {
        if (!isNumber(a)) throw 'params must be number!';
    }
    else if (pnl2(arguments)) {// because of pnl depends on
								// checkNumberType,so do this.
        for (var i = 0; i < arguments.length; i++) {
            checkNumberType(arguments[i]);
        }
    }
    return true;
}

function lt(a, b) {
    checkNumberType(a, b);
    return a < b;
}

function nlt(a, b) {
    return !lt(a, b);
}

// not less than
function pnl(arr, n) {
    return nlt(len(arr), n);// len(arr) >= n;
}

function gt(a, b) {
	checkNumberType(a, b);
	return a > b;
}


// greater than
function pgt(arr, n) {
    return gt(len(arr), n);// len(arr) > n;
}

function len(arr) {
    if (oExist(arr) && oExist(arr.length)) {
        return arr.length;
    }
    return 0;
}

function enumKeys(a) {
	var keys = Object.keys(a);
	if (fnExist(Object.getOwnPropertySymbols)) {
		keys = keys.concat(Object.getOwnPropertySymbols(a));
	}
	return keys;
}

// 适用任何类型
function eq(x, y) {
    // SameValue algorithm
    if (x === y) { // Steps 1-5, 7-10
        // Steps 6.b-6.e: +0 != -0
        return x !== 0 || 1 / x === 1 / y;
    } else {
        // Step 6.a: NaN == NaN
        return x !== x && y !== y;
    }
    
}

function whatType(a) {
    return typeof a;
}

function arrayLike2Array(a) {
    return Array.prototype.slice.call(a);
}


function simpleCopy(dest, src) {

    var pNum = len(arguments);
    if (pNum == 0) {
        return EMPTY_VALUES.OBJECT;
    }
    else if (pNum == 2) {    	
    	var keys = enumKeys(src);
        for (var i = 0; i < len(keys); i++) {
            var key = keys[i];
            dest[key] = src[key];
        }
    } else {// >2
        for (var i = 1; i < pNum; i++) {
            dest = simpleCopy(dest, arguments[i]);
        }
    }
    return dest;
}



function extendz(dest, src) {
	if (pgt(arguments, 2)) {
		// check itself multiple
		var check_m = new Map();

		for (var i = 0; i < arguments.length; i++) {
			var clazz = arguments[i];
			if (!isFunction(clazz)) {
				throw arguments[i] + ' is not a function!';
			}
			if (oExist(check_m.get(clazz))) {
				throw 'Class ' + clazz.name + ' only one!';
			} else {
				check_m.set(clazz, 1);
			}

		}
		var finalClass = dest;
		for (var i = arguments.length - 1; i > 0; i--) {
			finalClass = extendz(arguments[i - 1], arguments[i]);
		}
		return finalClass;
	} else if (isFunction(dest) && isFunction(src)) {

		if (dest === src) {
			throw 'Class cannot inherit from itself!';
		}

		
		var methods_obj = dest.prototype;
		var sup = methods_obj.__proto__;
		var supLevel = 0;
		while (oExist(sup)) {
			sup = sup.__proto__;
			supLevel++;
		}
		// 核心1：单继承判断！
		if (gt(supLevel, 1)) {
			throw dest.name + ' cannot support multiple inheritance!';
		}

		// 核心2：instanceof OK
		methods_obj.__proto__ = src.prototype;
		// dest.prototype = Object.create(src.prototype);
		// dest.prototype.constructor = dest;
		// methods_obj = dest.prototype;//re eq
		// 核心3：inherit static methods and fields
		var static_members = enumKeys(src);
		for (var i = 0; i < len(static_members); i++) {
			var static_member = static_members[i];
			dest[static_member] = src[static_member];
		}
		
		// 核心4：定义超类构造方法，base() = super()
		// 重复定义会报错，所以不用if去check base存在不存在
		// base 只有继承的派生类才有！
		// methods_obj代表本类的成员定义在其中
		// apply super => function
		Object
				.defineProperty(
						methods_obj,
						'parent',
						{
							// 要考虑构造函数执行顺序！！！
							// 从父类到子类依次执行构造
							// <=>super 每一个匿名函数都是新的
							value : function() {
								var supCon = this.parent.caller;
								if (fnExist(supCon)
										&& oExist(supCon.prototype)
										&& oExist(supCon.prototype.__proto__)
										&& fnExist(supCon.prototype.__proto__.constructor)) {
									supCon = supCon.prototype.__proto__.constructor;
									if (!eq(supCon, Object)
											&& this instanceof supCon) {
										supCon.apply(this, arguments);
									} else {
										throw "this.parent(...) is only called by subclass constructor";
									}
								} else {
									throw "this.parent(...) is only called by subclass constructor";
								}
							},
							configurable : false,
							enumerable : false,
							writable : false,
						});
		
		return dest;
	} else {
		throw 'First param and second param are all functions!';
	}
}


// implements interfaces
function implementz(clazz, inf) {
    if (pgt(arguments, 2)) {
        var finalClass = clazz;
        for (var i = arguments.length - 1; i > 0; i--) {
            finalClass = implementz(clazz, arguments[i]);
        }
        return finalClass;
    } else if (isFunction(clazz) && oExist(inf)) {
        simpleCopy(clazz.prototype, inf);
        return clazz;
    }
}



function mergez() {
    var last_inf = EMPTY_VALUES.OBJECT;
    var ps = arrayLike2Array(arguments);
    ps.unshift(last_inf);
    return simpleCopy.apply(null, ps);
}


// implements static interfaces
function staticImplementz(clazz, inf) {
    if (pgt(arguments, 2)) {
        var finalClass = clazz;
        for (var i = arguments.length - 1; i > 0; i--) {
            finalClass = staticImplementz(clazz, arguments[i]);
        }
        return finalClass;
    } else if (isFunction(clazz) && oExist(inf)) {
        return simpleCopy(clazz, inf);
    }
}





// === test ===

function A(){
	console.log("A");
	this.p=function(){
		console.log("this.p");
	}
}
A.staticp=function(){
	console.log("this.staticp");
}

function B(){
	this.parent();
	console.log("B");
}

extendz(B,A);

let b = new B();
b.p();
B.staticp();

// interfaces
var inst_string_interface = {
		toString() {
            if (oExist(JSON)) {
                return JSON.stringify(this);
            }
            return this;
        }
};

implementz(B,inst_string_interface);
console.log(b.toString());


var inst_invoke_interface = {

        
        // tool beginning:
        /**
		 * 返回对象属性值
		 */
        k: function (key) {
            return this[key];
        },
        /**
		 * 设置对象属性值
		 */
        kv: function (k, v) {
            this[k] = v;
            return this;
        },

        /**
		 * arguments f arguments 这个函数还是相当危险的，搞不好会增加底层对象的属性！ 总之这个很复杂啊，思路还不能理清。
		 * 如果不考虑容错性，很好写，但是为了够通用还是写写吧。
		 * 
		 * @param {string|arguments}
		 *            p0
		 * @param {arguments}
		 *            p1
		 */
        property: function (p0, p1) {
            // left => right then right => left
            // like this:a.b.c => a.b => a then a=>a.b =>b.c
            // san yuan
            var key = this.property.caller && this.property.caller.name;
            var args = key && p0;
            key = p1 && p0 || key;
            args = (key == p0 && p1) || args;
            // er yuan
            key = key || p0;
            args = args || EMPTY_VALUES.ARRAY;
            if (isStr(args) || !oExist(args.length)) {// string,
														// number,boolean,symbol;
														// later 3 not have
														// length!
                var tmp = EMPTY_VALUES.ARRAY;
                tmp.push(args);
                args = tmp;
            }
            if (pnl(args, 1)) {
                return this.kv(key, args[0]);
            }
            return this.k(key);
        },

        
        /**
		 * 调用底层的方法 f:方法名 告别"this.方法()"调用,直接this.fn(f,...params);
		 */
        fn: function (f) {
            if (pnl(arguments, 1) && fnExist(this.k(f))) {
                f = this.k(f);
                var ps = EMPTY_VALUES.ARRAY;
                for (var i = 1; i < arguments.length; i++) {
                    ps.push(arguments[i]);
                }
                return f.apply(this, ps);
            }

        },
        // 优秀API工具哈哈哈哈哈！！！简单包装api必备啊
        /**
		 * 直接根据上层方法名调用底层方法名相同的方法. 没有其他作用就是手写简单,也不用管参数传递的事情. args:Arguments
		 */
        invoke: function (aorf, a) {

            // one parameter,default init
            var f = this.invoke.caller && this.invoke.caller.name;
            var args = f && aorf;
            // two parameter :a exist
            f = a && aorf || f;
            args = (f == aorf && a) || args;
            // two parameter: a not exist
            f = f || aorf;
            args = args || EMPTY_VALUES.ARRAY;

            var ps = EMPTY_VALUES.ARRAY;
            ps[0] = f;
            
            for (var i = 0; i < args.length; i++) {
                ps.push(args[i]);
            }

            return this.fn.apply(this, ps);// this.fn的第一个参数必须是函数名。
        },

    };

implementz(B,inst_invoke_interface);
b.fn("p",1,2,3,4,5);
b.k("p");
b.kv("a",1);
console.log(b.k("a"));

var HashMap_static_impl = {
        DEFAULT_INITIAL_CAPACITY: 1 << 4,
        DEFAULT_LOAD_FACTOR: 0.75,
        MAXIMUM_CAPACITY: 1 << 30,
        // DIGIT_LIMIT: 10
};

staticImplementz(B, HashMap_static_impl);

console.log(B.MAXIMUM_CAPACITY);

//es6 new feature ...args
var static_of_interface = {

    // es6
    // valueOf: function (...d) {
    // return new this(...d);
    // },
    // of: function (...d) {
    // return this.valueOf(...d);
    // },
    // It's a very good way! "bind" is very good!
    of: function () {
        return new (this.bind.apply(this, [null].concat(arrayLike2Array(arguments))))()
    },
    valueOf: function () {
        return this.of.apply(this, arguments);
    }

    // not good way!
    // valueOf: function () {
    // var that = this;
    // for (var i = 0; i < arguments.length; i++) {
    // that = that.bind(null/*not useful*/, arguments[i]);
    // }

    // // 有个bug 就是在shell控制台的时候,function. 和 function.字符 会执行 new that!
    // return new that();

    // // var o = new this.apply(o,arguments);
    // // return o;
    // },
    // of: function () {
    // // var that = this;
    // // for (var i = 0; i < arguments.length; i++) {
    // // that = that.bind(this/*not useful*/, arguments[i]);
    // // }
    // // return new that();
    // return this.valueOf.apply(this, arguments);
    // },
};
staticImplementz(B,static_of_interface);
console.log(B.of(123));

console.log(b instanceof A);

